home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 1.iso
/
DEMON
/
RISCOS2
/
TCP_131S.ARC
/
c
/
ftpcli
< prev
next >
Wrap
Text File
|
1994-03-06
|
20KB
|
822 lines
/* FTP client (interactive user) code */
#define LINELEN 128 /* Length of command buffer */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include "global.h"
#include "mbuf.h"
#include "domain.h"
#include "netuser.h"
#include "icmp.h"
#include "timer.h"
#include "tcp.h"
#include "ftp.h"
#include "session.h"
#include "cmdparse.h"
#include "misc.h"
#include "Terminal.h"
extern struct session *current;
struct session *current_ftp;
extern char nospace[];
extern char badhost[];
static char notsess[] = "Not an FTP session!\r\n";
static char cantwrite[] = "Can't write %s\r\n";
static char cantread[] = "Can't read %s\r\n";
static int donothing(int, char **);
static int doftpcd(int, char **);
static int domkdir(int, char **);
static int dormdir(int, char **);
static int doascii(int, char **);
static int dobinary(int, char **);
static int dotype(int, char **);
static int doget(int, char **);
static int dohash(int, char **);
static int dolist(int, char **);
static int dols(int, char **);
static int doput(int, char **);
static void doreply(struct ftp *);
static void ftpsetup(struct ftp *, void (*)(), void (*)(), void (*)());
static void ftpccs(struct tcb *, char, char);
static void ftpcds(struct tcb *, char, char);
static int sndftpmsg(struct ftp *, char *, ...);
struct cmds ftpabort[] = {
"", donothing, 0, NULLCHAR, NULLCHAR,
"abort", doabort, 0, NULLCHAR, NULLCHAR,
"hash", dohash, 2, "hash lumpsize", NULLCHAR,
NULLCHAR, NULLFP, 0, "Only valid command is \"abort\"", NULLCHAR,
};
struct cmds ftpcmds[] = {
"", donothing, 0, NULLCHAR, NULLCHAR,
"ascii", doascii, 0, NULLCHAR, NULLCHAR,
"binary", dobinary, 0, NULLCHAR, NULLCHAR,
"cd", doftpcd, 2, "cd <directory>", NULLCHAR,
"dir", dolist, 0, NULLCHAR, NULLCHAR,
"list", dolist, 0, NULLCHAR, NULLCHAR,
"get", doget, 2, "get remotefile <localfile>", NULLCHAR,
"hash", dohash, 2, "hash lumpsize", NULLCHAR,
"image", dobinary, 0, NULLCHAR, NULLCHAR,
"ls", dols, 0, NULLCHAR, NULLCHAR,
"mkdir", domkdir, 2, "mkdir <directory>", NULLCHAR,
"nlst", dols, 0, NULLCHAR, NULLCHAR,
"rmdir", dormdir, 2, "rmdir <directory>", NULLCHAR,
"put", doput, 2, "put localfile <remotefile>", NULLCHAR,
"type", dotype, 0, NULLCHAR, NULLCHAR,
NULLCHAR, NULLFP, 0, NULLCHAR, NULLCHAR,
};
/* Handle top-level FTP command */
int doftp(int argc, char **argv)
{
struct session *s;
struct ftp *ftp;
struct tcb *tcb;
struct socket lsocket,fsocket;
lsocket.address = ip_addr;
lsocket.port = lport++;
if((fsocket.address = resolve(argv[1])) == 0)
{
cwprintf(NULL, badhost,argv[1]);
return 1;
}
if(argc < 3)
fsocket.port = FTP_PORT;
else
fsocket.port = atoi(argv[2]);
/* Allocate a session control block */
if((s = newsession()) == NULLSESSION)
{
cwprintf(NULL, "Too many sessions\r\n");
return 1;
}
if((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR)
strcpy(s->name,argv[1]);
s->type = FTP;
s->parse = (void(*)())ftpparse;
/* Allocate an FTP control block */
if((ftp = ftp_create(LINELEN)) == NULLFTP)
{
s->type = FREE;
cwprintf(NULL, nospace);
return 1;
}
ftp->window = Window_Open(s, "FTP", term_SIXTEEN | term_CARET);
if (ftp->window == NULL)
{
current = s;
}
ftp->state = STARTUP_STATE;
s->cb.ftp = ftp; /* Downward link */
ftp->session = s; /* Upward link */
s->window = ftp->window;
ftp->session->echo = TRUE;
/* Now open the control connection */
tcb = open_tcp(&lsocket, &fsocket, TCP_ACTIVE, 0, (void(*)())ftpccr, NULLVFP, (void(*)())ftpccs, 0,( char *)ftp);
ftp->control = tcb;
go(s);
return 0;
}
/* Parse user FTP commands */
void ftpparse(struct session *active, char *line, int16 len)
{
struct mbuf *bp;
if (active == NULL)
active = current;
current_ftp = active;
switch(active->cb.ftp->state)
{
case RECEIVING_STATE:
case SENDING_FILE_STATE:
/* The only command allowed in data transfer state is ABORT */
if (cmdparse(ftpabort, line, active->window) == -1)
{
cwprintf(active->window, "Transfer in progress; only ABORT is acceptable\r\n");
}
break;
case COMMAND_STATE:
/* Save it now because cmdparse modifies the original */
bp = qdata(line,len);
if (cmdparse(ftpcmds, line, active->window) == -1)
{
/* Send it direct */
if(bp != NULLBUF)
send_tcp(active->cb.ftp->control,bp);
else
cwprintf(active->window, nospace);
}
else
{
free_p(bp);
}
break;
case STARTUP_STATE: /* Startup up autologin */
cwprintf(active->window, "Not connected yet, ignoring %s\r\n",line);
break;
case USER_STATE: /* Got the user name */
line[len] = '\0';
sndftpmsg(active->cb.ftp,"USER %s",line);
break;
case PASS_STATE: /* Got the password */
cooked();
active->raw = FALSE;
line[len] = '\0';
sndftpmsg(active->cb.ftp,"PASS %s",line);
break;
}
}
/* Handle null line to avoid trapping on first command in table */
static int donothing(int argc, char **argv)
{
argc = argc;
argv = argv;
return 0;
}
/* Translate 'cd' to 'cwd' for convenience */
static int doftpcd(int argc, char **argv)
{
register struct ftp *ftp;
argc = argc;
ftp = current_ftp->cb.ftp;
return sndftpmsg(ftp,"CWD %s\r\n",argv[1]);
}
/* Translate 'mkdir' to 'xmkd' for convenience */
static int domkdir(int argc, char **argv)
{
register struct ftp *ftp;
argc = argc;
ftp = current_ftp->cb.ftp;
return sndftpmsg(ftp,"XMKD %s\r\n",argv[1]);
}
static int dohash(int argc, char **argv)
{
register struct ftp *ftp;
argc = argc;
ftp = current_ftp->cb.ftp;
ftp->hash = atoi(argv[1]);
ftp->last = 0;
return(0);
}
/* Translate 'rmdir' to 'xrmd' for convenience */
static int dormdir(int argc, char **argv)
{
register struct ftp *ftp;
argc = argc;
ftp = current_ftp->cb.ftp;
return sndftpmsg(ftp,"XRMD %s\r\n",argv[1]);
}
static int dobinary(int argc, char **argv)
{
register struct ftp *ftp;
argc = argc;
argv = argv;
ftp = current_ftp->cb.ftp;
ftp->type = IMAGE_TYPE;
sndftpmsg(ftp,"TYPE I\r\n");
return(0);
}
static int doascii(int argc, char **argv)
{
register struct ftp *ftp;
argc = argc;
argv = argv;
ftp = current_ftp->cb.ftp;
ftp->type = ASCII_TYPE;
sndftpmsg(ftp,"TYPE A\r\n");
return(0);
}
/* Handle "type" command from user */
static int dotype(int argc, char **argv)
{
register struct ftp *ftp;
ftp = current_ftp->cb.ftp;
if (argc < 2)
{
switch(ftp->type)
{
case IMAGE_TYPE:
cwprintf(ftp->window, "Image\r\n");
break;
case ASCII_TYPE:
cwprintf(ftp->window, "Ascii\r\n");
break;
}
return 0;
}
switch(*argv[1])
{
case 'i':
case 'b':
ftp->type = IMAGE_TYPE;
sndftpmsg(ftp,"TYPE I\r\n");
break;
case 'a':
ftp->type = ASCII_TYPE;
sndftpmsg(ftp,"TYPE A\r\n");
break;
case 'l':
ftp->type = IMAGE_TYPE;
sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]);
break;
default:
cwprintf(ftp->window, "Invalid type %s\r\n",argv[1]);
return 1;
}
return 0;
}
/* Start receive transfer. Syntax: get <remote name> [<local name>] */
static int doget(int argc, char **argv)
{
char *remotename, *temp, *cp, localname[256];
register struct ftp *ftp;
char *mode;
ftp = current_ftp->cb.ftp;
if (ftp == NULLFTP)
{
cwprintf(ftp->window, notsess);
return 1;
}
remotename = argv[1];
if (argc < 3)
{
temp = strdup(remotename);
while (cp = strpbrk(temp, "\"[]|<>+=;,."), cp != NULL)
*cp = '/';
if (strlen(temp) > 10)
temp[10] = '\0';
sprintf(localname, "ftp:%s", temp);
free(temp);
}
else
{
if (strchr(argv[2], '.'))
strcpy(localname, argv[2]);
else
sprintf(localname, "ftp:%s", argv[2]);
}
if (ftp->fp != NULLFILE && ftp->fp != stdout)
fclose(ftp->fp);
ftp->fp = NULLFILE;
if(ftp->type == IMAGE_PENDING)
{
ftp->type = IMAGE_TYPE ;
sndftpmsg(ftp,"TYPE I\r\n");
}
if (ftp->type == IMAGE_TYPE)
mode = binmode[WRITE_BINARY];
else
mode = "w";
if ((ftp->fp = fopen(localname,mode)) == NULLFILE)
{
cwprintf(ftp->window, cantwrite,localname);
return 1;
}
ftp->last = 0;
ftp->state = RECEIVING_STATE;
ftpsetup(ftp,(void(*)())ftpdr, NULLVFP, (void(*)())ftpcds);
/* Generate the command to start the transfer */
return sndftpmsg(ftp,"RETR %s\r\n",remotename);
}
/* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
static int dolist(int argc, char **argv)
{
char localname[256];
register struct ftp *ftp;
ftp = current_ftp->cb.ftp;
if(ftp == NULLFTP)
{
cwprintf(ftp->window, notsess);
return 1;
}
if(ftp->fp != NULLFILE && ftp->fp != stdout)
fclose(ftp->fp);
ftp->fp = NULLFILE;
if(argc < 3)
{
ftp->fp = stdout;
}
else
{
if (strchr(argv[2], '.'))
strcpy(localname, argv[2]);
else
sprintf(localname, "ftp:%s", argv[2]);
if ((ftp->fp = fopen(argv[2], "w")), ftp->fp == NULLFILE)
{
cwprintf(ftp->window, cantwrite,argv[2]);
return 1;
}
}
ftp->state = RECEIVING_STATE;
ftpsetup(ftp,(void(*)())ftpdr,NULLVFP,(void(*)())ftpcds);
/* Generate the command to start the transfer
It's done this way to avoid confusing the 4.2 FTP server
if there's no argument */
if(argc > 1)
{
if ( ftp->type == IMAGE_TYPE )
{
ftp->type = IMAGE_PENDING ;
sndftpmsg(ftp,"TYPE A\r\n");
}
return sndftpmsg(ftp,"LIST %s\r\n",argv[1]);
}
else
{
if ( ftp->type == IMAGE_TYPE )
{
ftp->type = IMAGE_PENDING ;
sndftpmsg(ftp,"TYPE A\r\n");
}
return sndftpmsg(ftp,"LIST\r\n","");
}
}
/* Abbreviated (name only) list of remote directory.
* Syntax: ls <remote directory/file> [<local name>]
*/
static int dols(int argc, char **argv)
{
char localname[256];
register struct ftp *ftp;
ftp = current_ftp->cb.ftp;
if(ftp == NULLFTP)
{
cwprintf(ftp->window, notsess);
return 1;
}
if(ftp->fp != NULLFILE && ftp->fp != stdout)
fclose(ftp->fp);
ftp->fp = NULLFILE;
if(argc < 3)
{
ftp->fp = stdout;
}
else
{
if (strchr(argv[2], '.'))
strcpy(localname, argv[2]);
else
sprintf(localname, "ftp:%s", argv[2]);
if ((ftp->fp = fopen(argv[2], "w")), ftp->fp == NULLFILE)
{
cwprintf(ftp->window, cantwrite,argv[2]);
return 1;
}
}
ftp->state = RECEIVING_STATE;
ftpsetup(ftp,(void(*)())ftpdr,NULLVFP,(void(*)())ftpcds);
/* Generate the command to start the transfer */
if(argc > 1)
{
if ( ftp->type == IMAGE_TYPE )
{
ftp->type = IMAGE_PENDING ;
sndftpmsg(ftp,"TYPE A\r\n");
}
return sndftpmsg(ftp,"NLST %s\r\n",argv[1]);
}
else
{
if ( ftp->type == IMAGE_TYPE )
{
ftp->type = IMAGE_PENDING ;
sndftpmsg(ftp,"TYPE A\r\n");
}
return sndftpmsg(ftp,"NLST\r\n","");
}
}
/* Start transmit. Syntax: put <local name> [<remote name>] */
static int doput(int argc, char **argv)
{
char *remotename,*localname;
char *mode;
struct ftp *ftp;
if((ftp = current_ftp->cb.ftp) == NULLFTP)
{
cwprintf(ftp->window, notsess);
return 1;
}
localname = argv[1];
if(argc < 3)
remotename = localname;
else
remotename = argv[2];
if(ftp->fp != NULLFILE && ftp->fp != stdout)
fclose(ftp->fp);
if(ftp->type == IMAGE_PENDING)
{
ftp->type = IMAGE_TYPE ;
sndftpmsg(ftp,"TYPE I\r\n");
}
if(ftp->type == IMAGE_TYPE)
mode = binmode[READ_BINARY];
else
mode = "r";
if((ftp->fp = fopen(localname,mode)) == NULLFILE)
{
cwprintf(ftp->window, cantread,localname);
return 1;
}
ftp->last = 0;
ftp->state = SENDING_FILE_STATE;
ftpsetup(ftp, NULLVFP, (void(*)())ftpdt, (void(*)())ftpcds);
/* Generate the command to start the transfer */
return sndftpmsg(ftp,"STOR %s\r\n",remotename);
}
/* Abort a GET or PUT operation in progress. Note: this will leave
* the partial file on the local or remote system
*/
int doabort(int argc, char **argv)
{
register struct ftp *ftp;
argc = argc;
argv = argv;
ftp = current_ftp->cb.ftp;
/* Close the local file */
if(ftp->fp != NULLFILE && ftp->fp != stdout)
fclose(ftp->fp);
ftp->fp = NULLFILE;
switch(ftp->state)
{
case SENDING_FILE_STATE:
/* Send a premature EOF.
Unfortunately we can't just reset the connection
since the remote side might end up waiting forever
for us to send something. */
close_tcp(ftp->data);
cwprintf(ftp->window, "Put aborted\r\n");
break;
case RECEIVING_STATE:
/* Just exterminate the data channel TCB; this will
* generate a RST on the next data packet which will
* abort the sender
*/
del_tcp(ftp->data);
ftp->data = NULLTCB;
cwprintf(ftp->window, "Get aborted\r\n");
break;
}
ftp->state = COMMAND_STATE;
return 0;
}
/* create data port, and send PORT message */
static void ftpsetup(struct ftp *ftp, void (*recv)(),
void (*send)(), void (*state)())
{
struct socket lsocket;
struct mbuf *bp;
lsocket.address = ip_addr;
lsocket.port = lport++;
/* Compose and send PORT a,a,a,a,p,p message */
if((bp = alloc_mbuf(35)) == NULLBUF)
{ /* 5 more than worst case */
cwprintf(ftp->window, nospace);
return;
}
/* I know, this looks gross, but it works! */
sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
hibyte(hiword(lsocket.address)),
lobyte(hiword(lsocket.address)),
hibyte(loword(lsocket.address)),
lobyte(loword(lsocket.address)),
hibyte(lsocket.port),
lobyte(lsocket.port));
bp->cnt = strlen(bp->data);
send_tcp(ftp->control,bp);
/* Post a listen on the data connection */
ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,
recv,send,state,0,(char *)ftp);
}
/* FTP Client Control channel Receiver upcall routine */
void ftpccr(register struct tcb *tcb, int16 cnt)
{
extern int ttyflow;
struct mbuf *bp;
struct ftp *ftp;
char c;
if((ftp = (struct ftp *)tcb->user) == NULLFTP)
{
/* Unknown connection; kill it */
close_tcp(tcb);
return;
}
/* Hold output if we're not the current session */
if (ftp->window == NULL && (mode != CONV_MODE || current == NULLSESSION
|| ttyflow == 0 || current->cb.ftp != ftp))
return;
if(recv_tcp(tcb,&bp,cnt) > 0)
{
while(pullone(&bp,&c) == 1)
{
switch(c)
{
case '\n': /* Complete line; process it */
ftp->buf[ftp->cnt] = '\0';
doreply(ftp);
ftp->cnt = 0;
break;
default:
if(ftp->cnt != LINELEN-1)
ftp->buf[ftp->cnt++] = c;
break;
}
}
}
}
/* Process replies from the server */
static void doreply(register struct ftp *ftp)
{
char **s;
cwprintf(ftp->window, "%s\r\n",ftp->buf);
if(ftp->cnt < 3)
return;
if(ftp->buf[3] == '-')
return;
switch(ftp->state)
{
case SENDING_FILE_STATE:
case RECEIVING_STATE:
if (ftp->buf[0] == '5') doabort(0, s);
break;
case STARTUP_STATE:
if (strncmp(ftp->buf, "220", 3) == 0)
{
ftp->state = USER_STATE;
cwprintf(ftp->window, "Enter user name: ");
}
else ftp->state = COMMAND_STATE;
break;
case USER_STATE:
if (strncmp(ftp->buf, "331", 3) == 0)
{
ftp->state = PASS_STATE;
noecho();
ftp->session->echo = FALSE;
cwprintf(ftp->window, "Password: ");
}
else ftp->state = COMMAND_STATE;
break;
case PASS_STATE:
echo();
ftp->session->echo = TRUE;
ftp->state = COMMAND_STATE;
case COMMAND_STATE:
cwprintf(ftp->window, "ftp> ");
break;
}
}
/* FTP Client Control channel State change upcall routine */
static void ftpccs(register struct tcb *tcb, char old, char new)
{
extern int ttyflow;
struct ftp *ftp;
char notify = 0;
extern char *tcpstates[];
extern char *reasons[];
extern char *unreach[];
extern char *exceed[];
old = old;
/* Can't add a check for unknown connection here, it would loop
* on a close upcall! We're just careful later on.
*/
ftp = (struct ftp *)tcb->user;
if (ftp->window || (ttyflow && current != NULLSESSION && current->cb.ftp == ftp))
notify = 1;
switch(new)
{
case CLOSE_WAIT:
if(notify)
cwprintf(ftp->window, "%s\r\n",tcpstates[new]);
close_tcp(tcb);
break;
case CLOSED: /* heh heh */
if (notify)
{
cwprintf(ftp->window, "%s (%s",tcpstates[new],reasons[tcb->reason]);
if (tcb->reason == NETWORK)
{
switch(tcb->type)
{
case DEST_UNREACH:
cwprintf(ftp->window, ": %s unreachable",unreach[tcb->code]);
break;
case TIME_EXCEED:
cwprintf(ftp->window, ": %s time exceeded",exceed[tcb->code]);
break;
}
}
cwprintf(ftp->window, ")\r\n");
cmdmode();
}
del_tcp(tcb);
if(ftp != NULLFTP)
{
if (ftp->window)
{
ftp->window->Attr = ATTR_REVERSE;
ftp->window->Flags.flags.dont_destroy = FALSE;
ftp->window->Session = NULL;
cwprintf(ftp->window, "\nThis session has finished, please close the window\n");
}
ftp_delete(ftp);
}
break;
default:
if(notify)
cwprintf(ftp->window, "%s\r\n",tcpstates[new]);
break;
}
}
/* FTP Client Data channel State change upcall handler */
static void ftpcds(struct tcb *tcb, char old, char new)
{
extern int ttyflow;
struct ftp *ftp;
old = old;
if ((ftp = (struct ftp *)tcb->user) == NULLFTP)
{
/* Unknown connection, kill it */
close_tcp(tcb);
return;
}
switch(new)
{
case FINWAIT2:
case TIME_WAIT:
if(ftp->state == SENDING_FILE_STATE)
{
/* We've received an ack of our FIN, so
* return to command mode
*/
ftp->state = COMMAND_STATE;
if(ftp->window != NULL || (ttyflow && current != NULLSESSION && current->cb.ftp == ftp))
{
if ( ftp->hash > 0 )
cwputchar(ftp->window, '\n');
cwprintf(ftp->window, "Put complete, %lu bytes sent\r\n",
tcb->snd.una - tcb->iss - 2);
}
}
break;
case CLOSE_WAIT:
close_tcp(tcb);
if (ftp->state == RECEIVING_STATE)
{
/* End of file received on incoming file */
if(ftp->fp != stdout)
fclose(ftp->fp);
ftp->fp = NULLFILE;
ftp->state = COMMAND_STATE;
if (ftp->window != NULL || (ttyflow && current != NULLSESSION && current->cb.ftp == ftp))
{
if ( ftp->hash > 0 )
cwputchar(ftp->window, '\n');
cwprintf(ftp->window, "Get complete, %lu bytes received\r\n",
tcb->rcv.nxt - tcb->irs - 2);
}
}
break;
case CLOSED:
ftp->data = NULLTCB;
del_tcp(tcb);
break;
}
}
/* Send a message on the control channel */
static int sndftpmsg(struct ftp *ftp, char *fmt, ...)
{
va_list argptr;
struct mbuf *bp;
int16 len;
va_start(argptr,fmt);
len = strlen(fmt) + strlen(va_arg(argptr,char *)) + 10; /* fudge factor */
va_end(argptr);
if((bp = alloc_mbuf(len)) == NULLBUF)
{
cwprintf(ftp->window, nospace);
return 1;
}
va_start(argptr,fmt);
vsprintf(bp->data,fmt,argptr);
va_end(argptr);
bp->cnt = strlen(bp->data);
send_tcp(ftp->control,bp);
return 0;
}